package me.sdevil507.org.service.impl;

import com.google.common.collect.Lists;
import com.querydsl.core.types.ExpressionUtils;
import com.querydsl.core.types.Predicate;
import com.querydsl.core.types.Projections;
import com.querydsl.jpa.impl.JPAQueryFactory;
import lombok.extern.slf4j.Slf4j;
import me.sdevil507.biz.common.exceptions.RoleCodeExistedException;
import me.sdevil507.biz.common.exceptions.RoleNameExistedException;
import me.sdevil507.org.dto.OrgResourceRelDto;
import me.sdevil507.org.dto.OrgRoleAllotResourcesParamsDto;
import me.sdevil507.org.dto.OrgRoleParamsDto;
import me.sdevil507.org.mapper.OrgRoleMapper;
import me.sdevil507.org.po.*;
import me.sdevil507.org.repository.*;
import me.sdevil507.org.service.OrgRoleService;
import me.sdevil507.platform.po.PlatResourcePo;
import me.sdevil507.platform.po.QPlatResourcePo;
import me.sdevil507.platform.repository.PlatResourceRepository;
import me.sdevil507.platform.service.PlatResourceService;
import me.sdevil507.supports.jpa.PageParamsDTO;
import me.sdevil507.supports.jpa.SortParamsDTO;
import me.sdevil507.supports.shiro.enums.RoleType;
import me.sdevil507.supports.shiro.helper.OrgAccountHelper;
import me.sdevil507.supports.shiro.realm.OrgUsernamePasswordRealm;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import javax.annotation.Resource;
import java.util.*;

/**
 * 机构角色表[OrgRoleServiceImpl]
 *
 * @author sdevil507
 * created on 2020-06-23 18:15:44
 */
@Service
@Slf4j
public class OrgRoleServiceImpl implements OrgRoleService {

    @Resource
    private OrgRoleRepository orgRoleRepository;

    @Resource
    private OrgRepository orgRepository;

    @Resource
    private OrgUserRepository orgUserRepository;

    @Resource
    private OrgResourceRepository orgResourceRepository;

    @Resource
    private OrgRoleResourceRelRepository orgRoleResourceRelRepository;

    @Resource
    private PlatResourceRepository platResourceRepository;

    @Autowired
    private OrgRoleMapper orgRoleMapper;

    @Autowired
    private OrgUsernamePasswordRealm orgUsernamePasswordRealm;

    @Autowired
    private PlatResourceService platResourceService;

    @Autowired
    private JPAQueryFactory jpaQueryFactory;

    @Override
    public Page<OrgRolePo> readAllByPage(OrgRoleParamsDto searchParamsDto, PageParamsDTO pageParamsDto, SortParamsDTO sortParamsDto) {
        QOrgRolePo qOrgRolePo = QOrgRolePo.orgRolePo;

        // 开启谓语查询条件
        Predicate predicate = qOrgRolePo.id.isNotNull();

        if(searchParamsDto.getDeptId() != null){
            predicate = ExpressionUtils.and(predicate, qOrgRolePo.deptId.eq(searchParamsDto.getDeptId()));
        }else{
            predicate = ExpressionUtils.and(predicate, qOrgRolePo.deptId.eq(OrgAccountHelper.getDeptId()));
        }

        String name = searchParamsDto.getName();
        if (!StringUtils.isEmpty(name)) {
            predicate = ExpressionUtils.and(predicate, qOrgRolePo.name.like("%" + name + "%"));
        }

        String userPhoneNumber = searchParamsDto.getUserPhoneNumber();
        if (!StringUtils.isEmpty(userPhoneNumber)) {
            predicate = ExpressionUtils.and(predicate, qOrgRolePo.users.any().phoneNumber.like("%" + userPhoneNumber + "%"));
        }

        String userRealName = searchParamsDto.getUserRealName();
        if (!StringUtils.isEmpty(userRealName)) {
            predicate = ExpressionUtils.and(predicate, qOrgRolePo.users.any().realName.like("%" + userRealName + "%"));
        }

        // 排序
        Sort sort = sortParamsDto.getSort();
        if (null == sort) {
            sort = Sort.by(new Sort.Order(Sort.Direction.ASC, "sortNum"));
        }

        // 执行返回
        Pageable pageable = pageParamsDto.getPageableWithSort(sort);
        return orgRoleRepository.findAll(predicate, pageable);
    }

    @Override
    public List<OrgRolePo> searchOrgAllRoleData(OrgRoleParamsDto dto) {
        QOrgRolePo qOrgRolePo = QOrgRolePo.orgRolePo;
        QOrgDeptPo qOrgDeptPo = QOrgDeptPo.orgDeptPo;
        List<OrgRolePo> allRoleList = new ArrayList<>();
        this.getRoleListByDeptId(qOrgDeptPo, qOrgRolePo, dto.getDeptId(), allRoleList);

        return allRoleList;
    }

    //循环获取子部门下的角色
    private void getRoleListByDeptId(QOrgDeptPo qOrgDeptPo, QOrgRolePo qOrgRolePo, Long deptId, List<OrgRolePo> allRoleList){
        OrgDeptPo deptPo = jpaQueryFactory.selectFrom(qOrgDeptPo).where(qOrgDeptPo.id.eq(deptId)).fetchOne();
        if(deptPo != null){
            List<OrgRolePo> roleList = jpaQueryFactory.selectFrom(qOrgRolePo).where(qOrgRolePo.deptId.eq(deptId)).fetch();
            if(!CollectionUtils.isEmpty(roleList)){
                allRoleList.addAll(roleList);
            }
            if(!CollectionUtils.isEmpty(deptPo.getChildren())){
                deptPo.getChildren().forEach(childrenDept -> {
                    this.getRoleListByDeptId(qOrgDeptPo, qOrgRolePo, childrenDept.getId(), allRoleList);
                });
            }
        }
    }

    @Override
    public void create(OrgRoleParamsDto dto) throws RoleNameExistedException, RoleCodeExistedException {
        if (nameExisted(dto.getDeptId(), dto.getName(), null)) {
            throw new RoleNameExistedException("角色名称已经存在!");
        }

        if (codeExisted(dto.getDeptId(), dto.getCode(), null)) {
            throw new RoleCodeExistedException("角色编码已经存在!");
        }

        OrgRolePo orgRolePo = orgRoleMapper.dto2po(dto);
        // 查询出机构
//        OrgPo orgPo = orgRepository.findById(OrgAccountHelper.getOrgId()).orElse(null);
//        orgRolePo.setOrg(orgPo);
        orgRoleRepository.save(orgRolePo);
    }

    @Override
    public void update(OrgRoleParamsDto dto) throws RoleNameExistedException, RoleCodeExistedException {
        OrgRolePo po = orgRoleRepository.findById(dto.getId()).orElse(null);
        if (null != po) {
            if (nameExisted(dto.getDeptId(), dto.getName(), dto.getId())) {
                throw new RoleNameExistedException("角色名称已经存在!");
            }

            if (codeExisted(dto.getDeptId(), dto.getCode(), dto.getId())) {
                throw new RoleCodeExistedException("角色编码已经存在!");
            }
            orgRoleMapper.updatePoFromDto(dto, po);
            orgRoleRepository.save(po);
        }
    }

    @Override
    public void delete(OrgRoleParamsDto dto) {
        orgRoleRepository.deleteById(dto.getId());
        orgUsernamePasswordRealm.clearAllCachedAuthorizationInfo();
    }

    @Override
    public void allotUsers(Long roleId, List<Long> userIds) {
        OrgRolePo orgRolePo = orgRoleRepository.findById(roleId).orElse(null);
        if (null != orgRolePo) {
            // 查询出用户列表
            Set<OrgUserPo> users = orgUserRepository.findAllByIdIn(userIds);
            orgRolePo.setUsers(users);
            orgRoleRepository.save(orgRolePo);
            orgUsernamePasswordRealm.clearCachedAuthorizationInfo(OrgAccountHelper.getUsername());
        }
    }

    @Override
    public void allotResources(OrgRoleAllotResourcesParamsDto dto) {
        OrgRolePo orgRolePo = orgRoleRepository.findById(dto.getRoleId()).orElse(null);
        if (null != orgRolePo) {
            //先删除后新增
            orgRoleResourceRelRepository.deleteByRoleId(dto.getRoleId());
            //查询出资源列表
            Set<PlatResourcePo> resources = platResourceRepository.findAllByCodeIn(dto.getShowResourceCodes());
            //比较设置
            resources.forEach(platResourcePo -> {
                OrgRoleResourceRelPo pr = new OrgRoleResourceRelPo();
                pr.setRole(orgRolePo);
                pr.setResource(platResourcePo);
                pr.setModuleShow(true);
                pr.setModuleAuth(dto.getAuthResourceCodes().contains(platResourcePo.getCode()));
                orgRoleResourceRelRepository.save(pr);
            });
        }
    }

    @Override
    public void allotOrgs(Long roleId, List<Long> orgIds) {
        OrgRolePo orgRolePo = orgRoleRepository.findById(roleId).orElse(null);
        if (null != orgRolePo) {
            // 查询出用户列表
            Set<OrgPo> orgs = orgRepository.findAllByIdIn(orgIds);
            orgRolePo.setOrgs(orgs);
            orgRoleRepository.save(orgRolePo);
        }
    }

    @Override
    public List<OrgUserPo> readUsersById(Long id) {
        OrgRolePo orgRolePo = orgRoleRepository.findById(id).orElse(null);
        if (orgRolePo != null) {
            return new ArrayList<>(orgRolePo.getUsers());
        } else {
            return null;
        }
    }

    @Override
    public List<OrgRoleResourceRelPo> readResourcesById(Long id) {
        OrgRolePo orgRolePo = orgRoleRepository.findById(id).orElse(null);
        if (orgRolePo != null) {
            return new ArrayList<>(orgRolePo.getResourceRel());
        } else {
            return null;
        }
    }

//    @Override
//    public Set<String> findPermissions() {
//        QOrgRolePo qOrgRolePo = QOrgRolePo.orgRolePo;
//        Predicate predicate = qOrgRolePo.id.isNotNull()
//                .and(qOrgRolePo.users.any().id.eq(OrgAccountHelper.getUserId()))
//                .and(qOrgRolePo.org.id.eq(OrgAccountHelper.getOrgId()));
//        List<OrgRolePo> roles = Lists.newArrayList(orgRoleRepository.findAll(predicate));
//        Set<String> permissions = new HashSet<>();
//        for (OrgRolePo role : roles) {
//            Set<OrgResourcePo> resources = role.getResources();
//            for (OrgResourcePo resource : resources) {
//                permissions.add(resource.getCode());
//            }
//        }
//        return permissions;
//    }

    @Override
    public Set<String> findPermissions() {

        Set<String> permissionCodes = new HashSet<>();

        if(RoleType.PLATFORM.name().equals(OrgAccountHelper.getOrgType())){
            permissionCodes.addAll(this.findOrgPermissions());
            permissionCodes.addAll(this.findExpertPermissions());
        }else if(RoleType.MGR.name().equals(OrgAccountHelper.getOrgType())){
            permissionCodes.addAll(this.findOrgPermissions());
        }else if(RoleType.EXPERT.name().equals(OrgAccountHelper.getOrgType())){
            permissionCodes.addAll(this.findExpertPermissions());
        }
        return permissionCodes;
    }


    private Set<String> findOrgPermissions() {
        QOrgRolePo qOrgRolePo = QOrgRolePo.orgRolePo;
        Predicate predicate = qOrgRolePo.id.isNotNull()
                .and(qOrgRolePo.users.any().id.eq(OrgAccountHelper.getUserId()));
        List<OrgRolePo> roles = Lists.newArrayList(orgRoleRepository.findAll(predicate));
        Set<String> permissions = new HashSet<>();
        for (OrgRolePo role : roles) {
            Set<OrgRoleResourceRelPo> resources = role.getResourceRel();
            for (OrgRoleResourceRelPo resource : resources) {
                permissions.add(resource.getResource().getCode());
            }
        }
        return permissions;
    }

    private Set<String> findExpertPermissions() {
        return platResourceService.readAllExpertModuleCodeData();
    }

    @Override
    public Set<String> findRoles() {
        Long userId = OrgAccountHelper.getUserId();
        Long orgId = OrgAccountHelper.getOrgId();
        QOrgUserRelPo qOrgUserRelPo = QOrgUserRelPo.orgUserRelPo;
        List<OrgUserRelPo> orgUserRelPos = jpaQueryFactory.selectFrom(qOrgUserRelPo).where(qOrgUserRelPo.org.id.eq(orgId).and(qOrgUserRelPo.user.id.eq(userId))).fetch();
        Set<String> roles = new HashSet<>();
        if(!CollectionUtils.isEmpty(orgUserRelPos)){
            //如果是机构管理人员,增加管理人员权限字符串
            if (orgUserRelPos.get(0).isMgr()) {
                roles.add(RoleType.MGR.name());
            }
            // 如果是专家,增加专家的权限字符串
            if (orgUserRelPos.get(0).isExpert()) {
                roles.add(RoleType.EXPERT.name());
            }
            return roles;
        }
//        OrgUserPo orgUserPo = orgUserRepository.findById(userId).orElse(null);
//        if (orgUserPo != null) {
//            for (OrgUserRelPo orgUserRelPo : orgUserPo.getOrgUserRel()) {
//                if (OrgAccountHelper.getOrgId().equals(orgUserRelPo.getOrg().getId())) {
//                    // 如果是机构管理人员,增加管理人员权限字符串
//                    if (orgUserRelPo.isMgr()) {
//                        roles.add(RoleType.MGR.name());
//                    }
//
//                    // 如果是专家,增加专家的权限字符串
//                    if (orgUserRelPo.isExpert()) {
//                        roles.add(RoleType.EXPERT.name());
//                    }
//                    return roles;
//                }
//            }
//        }
        return null;
    }

    /**
     * 判断角色名称是否存在
     *
     * @param name 角色名称
     * @param id   角色id
     * @return 执行反馈
     */
    private boolean nameExisted(Long deptId, String name, Long id) {
        QOrgRolePo qOrgRolePo = QOrgRolePo.orgRolePo;
        Predicate predicate = qOrgRolePo.id.isNotNull()
                .and(qOrgRolePo.deptId.eq(deptId))
                .and(qOrgRolePo.name.eq(name));
        if (null != id) {
            predicate = ExpressionUtils.and(predicate, qOrgRolePo.id.ne(id));
        }
        return orgRoleRepository.exists(predicate);
    }

    /**
     * 判断角色编码是否存在
     *
     * @param code 编码
     * @param id   角色id
     * @return 执行反馈
     */
    private boolean codeExisted(Long deptId, String code, Long id) {
        QOrgRolePo orgRolePo = QOrgRolePo.orgRolePo;
        Predicate predicate = orgRolePo.id.isNotNull()
                .and(orgRolePo.deptId.eq(deptId))
                .and(orgRolePo.code.eq(code));
        if (null != id) {
            predicate = ExpressionUtils.and(predicate, orgRolePo.id.ne(id));
        }
        return orgRoleRepository.exists(predicate);
    }

    @CacheEvict(value = "userOrgIdList", key = "'userId_' + #p0")
    @Override
    public void deleteOrgsOfUserId(Long id) {

    }

    @Cacheable(cacheNames = "userOrgIdList", key = "'userId_' + #p0")
    @Override
    public Set<Long> findOrgs(Long userId) {
        log.info("userOrgIdList无缓存时执行下面代码，获取字典表数据,Time：" + new Date());

        OrgUserPo orgUserPo = orgUserRepository.findById(userId).orElse(null);
        if (orgUserPo != null) {
            Set<Long> orgIds = new HashSet<>();
            Set<OrgRolePo> roles = orgUserPo.getRoles();
            for (OrgRolePo role : roles) {
                Set<OrgPo> orgPos = role.getOrgs();
                for (OrgPo orgPo : orgPos) {
                    orgIds.add(orgPo.getId());
                }
            }
            return orgIds;
        } else {
            return Collections.emptySet();
        }
    }

    @CacheEvict(value = "userOrgIdListByClassify", key = "'userId_' + #p0")
    @Override
    public void deleteOrgsByClassifyOfUserId(Long userId) {
    }

    @Cacheable(cacheNames = "userOrgIdListByClassify", key = "'userId_' + #p0")
    @Override
    public Map<String, Set<Long>> findOrgsByClassify(Long userId){
        log.info("userOrgIdListByClassify无缓存时执行下面代码，获取字典表数据,Time：" + new Date());

        Map<String, Set<Long>> orgClassifyMap = new HashMap<>();
        OrgUserPo orgUserPo = orgUserRepository.findById(userId).orElse(null);
        if (orgUserPo != null) {
            //省重点
            Set<Long> pointOrgIds = new HashSet<>();
            //省培育
            Set<Long> fosterOrgIds = new HashSet<>();
            Set<OrgRolePo> roles = orgUserPo.getRoles();
            for (OrgRolePo role : roles) {
                Set<OrgPo> orgPos = role.getOrgs();
                for (OrgPo orgPo : orgPos) {
                    if(("7").equals(orgPo.getClassify())){
                        pointOrgIds.add(orgPo.getId());
                    }else if(("8").equals(orgPo.getClassify())){
                        fosterOrgIds.add(orgPo.getId());
                    }
                }
            }
            orgClassifyMap.put("point", pointOrgIds);
            orgClassifyMap.put("foster", fosterOrgIds);
            return orgClassifyMap;
        }
        return null;
    }


    @Override
    public List<OrgPo> readOrgsById(Long id) {
        OrgRolePo orgRolePo = orgRoleRepository.findById(id).orElse(null);
        if (orgRolePo != null) {
            return new ArrayList<>(orgRolePo.getOrgs());
        } else {
            return null;
        }
    }

    @Override
    public List<OrgResourceRelDto> getUserPermissionModule(Long userId) {
        QOrgRoleResourceRelPo qOrgRoleResourceRelPo = QOrgRoleResourceRelPo.orgRoleResourceRelPo;
        QPlatResourcePo qPlatResourcePo = QPlatResourcePo.platResourcePo;

        return jpaQueryFactory.select(
                Projections.constructor(OrgResourceRelDto.class,
                        qOrgRoleResourceRelPo.moduleShow,
                        qOrgRoleResourceRelPo.moduleAuth,
                        qPlatResourcePo.id,
                        qPlatResourcePo.parentId,
                        qPlatResourcePo.code,
                        qPlatResourcePo.title,
                        qPlatResourcePo.type,
                        qPlatResourcePo.classify,
                        qPlatResourcePo.serviceClassify,
                        qPlatResourcePo.path,
                        qPlatResourcePo.sortNum,
                        qPlatResourcePo.iconSvg,
                        qPlatResourcePo.tags,
                        qPlatResourcePo.description
                )
        ).from(qOrgRoleResourceRelPo).leftJoin(qPlatResourcePo).on(qOrgRoleResourceRelPo.resource.id.eq(qPlatResourcePo.id))
                .where(qOrgRoleResourceRelPo.role.users.any().id.eq(userId)).fetch();
    }
}